package top.byteio.common.log.aspect;

import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import com.alibaba.fastjson2.JSON;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import top.byteio.common.core.utils.IPAddressUtils;
import top.byteio.common.log.annotation.WebLog;
import top.byteio.common.log.pojo.WebLogDTO;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
* 统一日志处理切面
* @author lerryxia
*/
@Aspect
@Slf4j(topic = "web-log")
@Order(1)
@Component
public class WebLogAspect {

    /**
     * ..表示包及子包 该方法代表controller层的所有方法
     * Pointcut定义时，还可以使用&&、||、! 这三个运算
     */
    @Pointcut("@annotation(top.byteio.common.log.annotation.WebLog)")
    public void webLog() {
    }

    @Before("webLog()")
    public void doBefore(JoinPoint joinPoint) throws Throwable {
    }

    @AfterReturning(value = "webLog()", returning = "ret")
    public void doAfterReturning(Object ret) throws Throwable {
    }

    @Around("webLog()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        // 检查是否是HTTP请求上下文
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attributes == null) {
            // 非HTTP请求上下文，直接执行方法
            return joinPoint.proceed();
        }
        
        long startTime = System.currentTimeMillis();
        HttpServletRequest request = attributes.getRequest();
        //记录请求信息(通过Logstash传入Elasticsearch)
        Object result = joinPoint.proceed();
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();
        long endTime = System.currentTimeMillis();
        String urlStr = request.getRequestURL().toString();

        WebLog annotation = method.getAnnotation(WebLog.class);

        String description = annotation.msg();
        List<Object> parameter = getParameter(method, joinPoint.getArgs());
        WebLogDTO webLog = WebLogDTO.builder()
                .description(description)
                .basePath(StrUtil.removeSuffix(urlStr, URLUtil.url(urlStr).getPath()))
                .username(request.getRemoteUser())
                .ip(IPAddressUtils.getRequestIp(request))
                .method(request.getMethod())
                .parameter(JSON.toJSONString(parameter))
                .result(result)
                .startTime(startTime)
                .spendTime((endTime - startTime))
                .url(request.getRequestURL().toString())
                .uri(request.getRequestURI())
                .build();
        String json = JSON.toJSONString(webLog);
        log.info("{}", json);
        return result;
    }

    /**
 * 根据方法和传入的参数获取请求参数
 */
private List<Object> getParameter(Method method, Object[] args) {
    if (method == null || args == null) {
        return null;
    }

    List<Object> argList = new ArrayList<>();
    Parameter[] parameters = method.getParameters();

    Map<String, Object> requestParamMap = new HashMap<>(); // 用于统一存放 @RequestParam 参数
    boolean hasRequestParam = false;

    for (int i = 0; i < parameters.length; i++) {
        Parameter parameter = parameters[i];

        // 处理 @RequestBody 注解
        RequestBody requestBody = parameter.getAnnotation(RequestBody.class);
        if (requestBody != null) {
            argList.add(args[i]);
        }

        // 处理 @RequestParam 注解
        RequestParam requestParam = parameter.getAnnotation(RequestParam.class);
        if (requestParam != null) {
            String key = requestParam.value();
            if (StrUtil.isEmpty(key)) {
                key = parameter.getName(); // 注意需确保编译时带 -parameters 参数
            }
            requestParamMap.put(key, args[i]);
            hasRequestParam = true;
        }
    }

    if (hasRequestParam) {
        argList.add(requestParamMap); // 所有 RequestParam 合并为一个 map 添加一次即可
    }

    if (argList.isEmpty()) {
        return null;
    } else {
        return argList; // 直接返回 ArrayList，不再做 size 判断及特殊封装
    }
}


}